#pragma once
/*
模块说明：实现Rpc客户端端模块，整合Rpc_call,Rpc_requstor,Rpc_register模块，
整合到一起封装起来给客户使用
*/

#include "../Net.hpp"
#include "../Unit.hpp"
#include "../Dispatch.hpp"
#include "Rpc_Caller.hpp"
#include "Rpc_Register.hpp"
#include "Rpc_Topic.hpp"
// 1 提供服务注册的功能的客户端给提供服务的主机的使用

struct Hash
{
    size_t operator()(const Message::Address &add) const
    {
        std::string s1 = add.first + std::to_string(add.second);
        return std::hash<std::string>{}(s1); // 利用ip加端口组成的字符串计算哈希值
    }
};
namespace RpcClient
{
    class RegisterClient
    {
    public:
        // 服务中心的主机信息
        RegisterClient(const std::string &ip, uint16_t port)
            : _requestor(std::make_shared<Client::Resquestor>()),
              _Provider(std::make_shared<RegistProvider>(_requestor)), _Dispatch(std::make_shared<Dispactch>())
        {
            // 1 注册实际处理回应的回调函数给消息分发模块
            _Dispatch->RegisterHander(Rs_RqType::Service_Res,
                                      [this](const Abstract::BaseConnection::ConPtr &Con, const Abstract::BaseMessage::MesaPtr &Mptr)
                                      {
                                          _requestor->DealResponce(Con, Mptr);
                                      });
            // 2 创建客户端对象负责网络通信
            _Client = Net::BulidMuduoClient::build(ip, port);

            // 3 绑定客户端接收到消息的回调处理函数
            _Client->SetReady_CallBack(
                [this](const Abstract::BaseConnection::ConPtr &Con, const Abstract::BaseMessage::MesaPtr &Mptr)
                {
                    _Dispatch->MessageCallback(Con, Mptr);
                });
            // 4 客户端连接服务端
            _Client->ConnectServer();
        }
        // 客户端注册服务的接口
        bool RegisterMethod(const std::string &method, const Message::Address &Host)
        {
            return _Provider->RegisterMethod(_Client->GetConnection(), method, Host);
        }
        void ShutDown()
        {
            _Client->Shutdown();
        }

    private:
        Client::Resquestor::RPtr _requestor;  // 请求发送和处理回应(通过promise对象获取回应message)
        RegistProvider::RPPtr _Provider;      //
        Dispactch::DispaPtr _Dispatch;        // 回应分法模块
        Abstract::BaseClient::SerPtr _Client; // 网络通信模块
    };

    // 负责向服务中心发送服务发现的和接收来自服务中心的服务上下线的通知请求客户端
    class DiscoverClient
    {
    public:
        using DisCptr = std::shared_ptr<DiscoverClient>;
        // 服务中心的主机信息
        DiscoverClient(const std::string &ip, uint16_t port, const Discover::DelHostCallback &Cb)
            : _requestor(std::make_shared<Client::Resquestor>()),
              _discover(std::make_shared<Discover>(_requestor, Cb)), _Dispatch(std::make_shared<Dispactch>())
        {
            // 1 注册实际处理请求发现服务回应的回调函数和服务上下线请求 给消息分发模块
            _Dispatch->RegisterHander(Rs_RqType::Service_Res,
                                      [this](const Abstract::BaseConnection::ConPtr &Con, const Abstract::BaseMessage::MesaPtr &Mptr)
                                      {
                                          _requestor->DealResponce(Con, Mptr);
                                      });
            _Dispatch->RegisterHander(Rs_RqType::Service_Req,
                                      [this](const Abstract::BaseConnection::ConPtr &Con, const Abstract::BaseMessage::MesaPtr &Mptr)
                                      {
                                          _discover->DealServerRequest(Con, Mptr);
                                      });
            // 2 创建客户端对象负责网络通信
            _Client = Net::BulidMuduoClient::build(ip, port);

            // 3 绑定客户端接收到消息的回调处理函数
            _Client->SetReady_CallBack(
                [this](const Abstract::BaseConnection::ConPtr &Con, const Abstract::BaseMessage::MesaPtr &Mptr)
                {
                    _Dispatch->MessageCallback(Con, Mptr);
                });
            // 4 客户端连接服务端获取链接对象后返回函数调用
            _Client->ConnectServer();
        }
        // 客户端获取指定提供method服务的主机信息接口
        bool DiscoverMethod(const std::string &method, Message::Address &Host)
        {
            //ELOG("进入到获取供method服务的主机信息接口模块");
            return _discover->GetDiscoverService(_Client->GetConnection(), method, Host);
        }

    private:
        Client::Resquestor::RPtr _requestor;  // 请求发送和处理回应(通过promise对象获取回应message)
        Discover::DPtr _discover;             // 服务发现模块--进行服务发现请求和获取主机信息
        Dispactch::DispaPtr _Dispatch;        // 处理回应分发模块--把不同类型的回应消息交给不同的回调函数处理
        Abstract::BaseClient::SerPtr _Client; // 网络通信模块
    };

    // 负责向指定主机发送Rpc请求的客户端
    class RpcClient
    {
    public:
        using RpcClientPtr = std::shared_ptr<RpcClient>;
        // 提供服务的主机的信息,是否启用服务发现功能，如果启用则先进行一次服务发现
        // 然后再进行Rpc请求,启用则传入服务中心的主机信息
        RpcClient(bool Enable, const std::string &ip, uint16_t port)
            : _IsEnableDiscover(Enable), _requestor(std::make_shared<Client::Resquestor>()),
              _Dispatch(std::make_shared<Dispactch>()), _RpcCaller(std::make_shared<Rpc_Caller>(_requestor))
        {
            // 1 注册实际处理请求发现服务回应的回调函数和服务上下线请求 给消息分发模块
            _Dispatch->RegisterHander(Rs_RqType::RPC_Res,
                                      [this](const Abstract::BaseConnection::ConPtr &Con, const Abstract::BaseMessage::MesaPtr &Mptr)
                                      {
                                          _requestor->DealResponce(Con, Mptr);
                                      });
            if (_IsEnableDiscover == true)
            {
                // 如果启动服务调用，那么创建一个服务发现客户端去链接服务中心查询服务
                // 当前线程只负责链接获取链接对象，在创建客户端时其内部就已经创建了
                // 一个线程去监听获取的链接对象的就绪事件了。并且发送服务请求把 获取到的主机信息
                // 保存在其客户端对象内部,注册Rpc删除连接池中已经下线的主机的函数到Discover客户端中去
                // 当dis客户端接收到下线请求回调出来删除连接池对应的映射关系
                _Discover_Client = std::make_shared<DiscoverClient>(ip, port, [this](const Message::Address &Host) -> void
                                                                    { Remove(Host); });
            }
            else
            {
                // 没有启动服务查询客户端，那么ip和port就是提供Rpc调用的主机的信息
                _RpcClient = Net::BulidMuduoClient::build(ip, port);
                _RpcClient->SetReady_CallBack(
                    [this](const Abstract::BaseConnection::ConPtr &Con, const Abstract::BaseMessage::MesaPtr &Mptr)
                    {
                        _Dispatch->MessageCallback(Con, Mptr);
                    });
                // 4 客户端连接服务端获取链接对象后返回函数调用
                _RpcClient->ConnectServer();
            }
        }
        // 基于发送线程同步等待请求结果的发送方式，在发送完请求和阻塞等待返回结果
        bool call(const std::string &method, const Json::Value &params, Json::Value &result)
        {
            Abstract::BaseClient::SerPtr ClientPtr = GetRpcClient(method);
            if (ClientPtr.get() == nullptr)
            {
                //ELOG("该服务:%s没有主机能够提供", method.c_str());
                return false;
            }
            // 阻塞等待回应
            //ELOG("进入同步消息发送模块,请求方法名称%s", method.c_str());
            //std::cout << params["nums1"] << std::endl;
            return _RpcCaller->call(ClientPtr->GetConnection(), method, params, result);
        }

        // 发送线程基于future对象的模式异步获取Rpc回应,
        bool call(const std::string &method, const Json::Value &params, std::future<Json::Value> &result, std::string &uuid)
        {
            Abstract::BaseClient::SerPtr ClientPtr = GetRpcClient(method);
            if (ClientPtr.get() == nullptr)
            {
                ELOG("该服务:%s没有主机能够提供", method.c_str());
                return false;
            }
            // 异步等待回应--通过future对象获取回应
            return _RpcCaller->call(ClientPtr->GetConnection(), method, params, result, uuid);
        }

        // 基于用户传入的回调函数异步直接处理Rpc回应的计算结果
        bool call(const std::string &method, const Json::Value &params, std::string &uuid, const Rpc_Caller::RpcJsonResCallBack &Rb)
        {
            Abstract::BaseClient::SerPtr ClientPtr = GetRpcClient(method);
            if (ClientPtr.get() == nullptr)
            {
                ELOG("该服务:%s没有主机能够提供", method.c_str());
                return false;
            }
            // 阻塞等待回应
            return _RpcCaller->call(ClientPtr->GetConnection(), method, params, uuid, Rb);
        }
        void DelDescResq(const std::string &uuid)
        {
            _requestor->DelDescResq(uuid);
        }

    private:
        // 创建Rpc服务主机的信息对应的链接客户端去链接对应的主机获取链接对象加入到连接池中
        Abstract::BaseClient::SerPtr NewRpcClient(const Message::Address &add)
        {
            auto RpcClient = Net::BulidMuduoClient::build(add.first, add.second);
            RpcClient->SetReady_CallBack(
                [this](const Abstract::BaseConnection::ConPtr &Con, const Abstract::BaseMessage::MesaPtr &Mptr)
                {
                    _Dispatch->MessageCallback(Con, Mptr);
                });
            RpcClient->ConnectServer(); // 链接对应的主机获取链接对象保存在其内部
            PutRpcClient(add, RpcClient);
            return RpcClient;
        }
        // 从连接池中获取提供Rpc服务主机的信息对应的链接客户端
        Abstract::BaseClient::SerPtr GetRpcClient(const Message::Address &add)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if (_RpcClients.count(add))
            {
                return _RpcClients[add];
            }
            return Abstract::BaseClient::SerPtr();
        }

        Abstract::BaseClient::SerPtr GetRpcClient(const std::string &method)
        {
            //ELOG("Rpc客户端进入向服务中心发送服务查询请求模块");
            //Abstract::BaseClient::SerPtr ClientPtr;
            if (_IsEnableDiscover == true)
            {
                // 1 启动了服务查询客户端,那么这个客户端对象就已经创建了一个链接对象了(和服务中心链接上了获取了fd了)
                // 然后同时创建了一个监听线程去监听该fd了,获取到的主机信息都保存在该客户端对象内部了
                Message::Address Host;
                // 从服务发现客户端获取方法的提供主机信息，如果没有，服务发现客户端会发起一次服务发现请求
                // 给服务中心并且阻塞等待服务中心的回应如果还是没有能够提供的主机那就返回false
                bool ret = _Discover_Client->DiscoverMethod(method, Host);
                if (ret == false)
                {

                    return Abstract::BaseClient::SerPtr();
                }
                // 2 查看该主机有没有对应的链接客户端对象
                if (_RpcClients.count(Host) <= 0)
                {
                    // 没有则创建关系映射
                    auto ClientPtr = NewRpcClient(Host);
                    // 发送Rpc请求阻塞等待回应
                    return ClientPtr;
                }
                else
                {
                    return GetRpcClient(Host);
                }
                //
            }
            else
            {
                // 没有启动服务查询客户端，直接使用
                return _RpcClient;
            }
        }
        // 往连接池中添加一组链接和主机的映射关系
        void PutRpcClient(const Message::Address &add, const Abstract::BaseClient::SerPtr &RpcClient)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if (_RpcClients.count(add) <= 0)
            {
                _RpcClients.insert({add, RpcClient});
            }
        }
        // 从连接池中移除一组映射关系，当提供服务的主机下线时
        void Remove(const Message::Address &add)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if (_RpcClients.count(add))
            {
                _RpcClients.erase(add);
            }
        }

    private:
        bool _IsEnableDiscover;                   // 是否启用服务发现功能
        Client::Resquestor::RPtr _requestor;      // 请求发送和处理回应(通过promise对象获取回应message)
        DiscoverClient::DisCptr _Discover_Client; // 服务发现客户端
        Rpc_Caller::RpcCallPtr _RpcCaller;        // 组成发送Rpc请求模块

        Dispactch::DispaPtr _Dispatch;
        Abstract::BaseClient::SerPtr _RpcClient; // 不启用服务查询的Rpc客户端                                          // 回应分法模块
        std::mutex _mutex;                       // 基于长连接锁保护，多个线程客户端请求Rpc服务
        // 连接池模块--基于长连接模式的Rpc请求，主线程会创建多个和服务端的链接即获取多个fd,同时会创建多个线程去监听不同的fd
        // 的就绪事件然后调用回调函数，从Dic获取到指定主机消息后查看当前有没有和这个主机的链接对象
        // 有就用形成的没有就创建一个链接对象发起请求
        std::unordered_map<Message::Address, Abstract::BaseClient::SerPtr, Hash> _RpcClients;
    };

    // 该模块负责和主题服务中心建立连接然后通信，会创建一个线程去监听连接时申请的socket fd即连接对象
    class TopicClient
    {
    public:
        // 主题服务中心的主机信息
        TopicClient(const std::string &ip, uint16_t port)
            : _requestor(std::make_shared<Client::Resquestor>()),
              _topicmanager(std::make_shared<Topic_Manager::TopicManager>(_requestor)), _Dispatch(std::make_shared<Dispactch>())
        {
            // 1 注册实际处理回应的回调函数给消息分发模块
            _Dispatch->RegisterHander(Rs_RqType::TOPIC_Res,
                                      [this](const Abstract::BaseConnection::ConPtr &Con, const Abstract::BaseMessage::MesaPtr &Mptr)
                                      {
                                          _requestor->DealResponce(Con, Mptr);
                                      });
            // 注册处理主题内容推送请求的回调函数
            _Dispatch->RegisterHander(Rs_RqType::TOPIC_Req,
                                      [this](const Abstract::BaseConnection::ConPtr &Con, const Abstract::BaseMessage::MesaPtr &Mptr)
                                      {
                                          _topicmanager->DelpublishComment(Con, Mptr);
                                      });
            // 2 创建客户端对象负责网络通信
            _Client = Net::BulidMuduoClient::build(ip, port);

            // 3 绑定客户端接收到消息的回调处理函数
            _Client->SetReady_CallBack(
                [this](const Abstract::BaseConnection::ConPtr &Con, const Abstract::BaseMessage::MesaPtr &Mptr)
                {
                    _Dispatch->MessageCallback(Con, Mptr);
                });
            // 4 客户端连接服务端，连接成功返回socket fd即返回一个连接对象，然后创建一个线程去监听这个
            // 连接对象的就读绪事件
            _Client->ConnectServer();
            DLOG("连接成功");
        }
        // 创建一个主题
        bool create(const std::string &topicname)
        {
            return _topicmanager->create(_Client->GetConnection(), topicname);
            DLOG("创建主题成功");
        }
        bool remove(const std::string &topicname)
        {
            return _topicmanager->remove(_Client->GetConnection(), topicname);
        }
        // 订阅一个主题同时注册一个对应的处理接收该主题的内容时的回调函数
        bool subscribe(const std::string &topicname, const  Topic_Manager::TopicManager::DealTopicBack &Cb)
        {
             return _topicmanager->subscribe(_Client->GetConnection(), topicname,Cb);
        }
        bool cancel(const std::string &topicname)
        {
            return _topicmanager->cancel(_Client->GetConnection(), topicname);
        }
        // 推送主题的内容
        bool publish(const std::string &topicname, const std::string &msg)
        {
             return _topicmanager->publish(_Client->GetConnection(), topicname,msg);
        }
       
        void ShutDown()
        {
            _Client->Shutdown();
        }

    private:
        Client::Resquestor::RPtr _requestor;                    // 请求发送和处理回应(通过promise对象获取回应message)
        Topic_Manager::TopicManager::TManagerPtr _topicmanager; // 负责创建主题请求和接收主题推送请求
        Dispactch::DispaPtr _Dispatch;                          // 回应分法模块
        Abstract::BaseClient::SerPtr _Client;                   // 网络通信模块
    };
}
/*
基于长连接思想:一台主机的一个线程在第一次请求指定名称Rpc请求后不要关闭客户端连接
如果下一次再次请求该Rpc服务先到连接错池中程序有没有客户端连接着提供该服务的主机，
有就使用该链接发起请求，没有就创建一个客户端去请求，
*/